交叉验证,模型选择(cross validation)
很多时候,大家会利用各种方法建立不同的统计模型,诸如普通的cox回归,利用Lasso方法建立的cox回归,或者稳健的cox回归;或者说利用不同的变量建立不同的模型,诸如模型一只考虑了三个因素、模型二考虑了四个因素,最后对上述模型选择(评价)的时候,或者是参数择优的时候,通常传统统计学方法中会用AIC,BIC、拟合优度-2logL,或者预测误差最小等准则来选择最优模型;而最新的文献中都会提到一种叫交叉验证(Cross validation)的方法,或者会用到一种将原始数据按照样本量分为两部分三分之二用来建模,三分之一用来验证的思路(临床上有医生称为内部验证),再或者利用多中心数据,一个中心数据用来建模,另外一个中心数据用来验证(临床上称为外部验证),这些都是什么?总结一下自己最近看的文献和书籍,在这里简单介绍下,仅供参考。
一、交叉验证的概念
交叉验证(Cross validation),有时亦称循环估计,是一种统计学上将数据样本切割成较小子集的实用方法。于是可以先在一个子集上做建模分析,而其它子集则用来做后续对此分析的效果评价及验证。一开始的子集被称为训练集(Train set)。而其它的子集则被称为验证集(Validationset)或测试集(Test set)。交叉验证是一种评估统计分析、机器学习算法对独立于训练数据的数据集的泛化(普遍适用性)能力(Generalize).例如下图文献中,原始数据集中449例观测,文献中将数据集分为了训练集(Primary Cohort)367例,验证集(Validation Cohort)82例。
二、交叉验证的原理及分类
假设利用原始数据可以建立 n 个统计模型,这 n 个模型的集合是M={M1,M2,…,Mn},比如我们想做回归,那么简单线性回归、logistic回归、随机森林、神经网络等模型都包含在M中。目标任务就是要从M中选择最好的模型。
(1)简单交叉验(hold-outcross validation)
假设训练集使用T来表示。如果想使用预测误差最小来度量模型的好坏,那么可以这样来选择模型:
1).使用T来训练每一个M,训练出参数(模型的系数)后,也就可以得到模型方程Fi。(比如,线性模型中得到系数ai后,也就得到了模型Fi(x)=aTx);
2).选择预测误差最小的模型。
遗憾的是这个算法不可行,比如我们需要拟合一些样本点,使用高阶的多项式回归肯定比线性回归错误率要小,偏差小,但是方差却很大,会过度拟合。因此,我们改进算法如下:
1).从全部的训练数据T中随机选择70%的样本作为训练集Ttrain,剩余的30%作为测试集Tcv。
2).在Ttrain上训练每一个M,得到模型Fi。
3).在Tcv上测试每一个Fi,得到相应的预测误差e。
4).选择具有最小预测误差的作为最佳模型。
这种方法称为hold-outcross validation或者称为简单交叉验证。
由于测试集是和训练集中是两个世界的,因此可以认为这里的预测误差接近于真实误差(generalizationerror)。这里测试集的比例一般占全部数据的1/4-1/3。30%是典型值。
还可以对模型作改进,当选出最佳的模型M后,再在全部数据T上做一次训练,显然训练数据越多,模型参数越准确。
简单交叉验证方法的弱点在于得到的最佳模型是在70%的训练数据上选出来的,不代表在全部训练数据上是最佳的。还有当训练数据本来就很少时,再分出测试集后,训练数据就太少了。其实严格意义来说Hold-OutMethod并不能算是CV,因为这种方法没有达到交叉的思想,由于是随机的将原始数据分组,所以最后验证集分类准确率的高低与原始数据的分组有很大的关系,所以这种方法得到的结果其实并不具有说服性.
(2)k-折叠交叉验证(k-fold cross validation)
进一步对简单交叉验证方法再做一次改进,如下:
1).将全部训练集T分成k个不相交的子集,假设T中的训练样例个数为m,那么每一个子集有m/k个训练样例,相应的子集称作{T1,T2,…, Tk}。
2).每次从模型集合M中拿出来一个Mi,然后在训练子集中选择出k-1个{T1,T2,Tj-1,Tj+1…,Tk}(也就是每次只留下一个Tj),使用这k-1个子集训练Mi后,得到假设函数Fij。最后使用剩下的一份Tj作测试,得到预测误差eij。
3).由于我们每次留下一个Tj(j从1到k),因此会得到k个预测误差,那么对于一个Mi,它的预测误差是这k个预测误差的平均。
4).选出平均经验错误率最小的Mi,然后使用全部的T再做一次训练,得到最后的模型Fi。
此方法称为k-fold cross validation(k-折叠交叉验证)说白了,这个方法就是将简单交叉验证的测试集改为1/k,每个模型训练k次,测试k次,预测误差为k次的平均。K一般大于等于2,实际操作时一般从3开始取,只有在原始数据集合数据量小的时候才会尝试取2.K-CV可以有效的避免过学习以及欠学习状态的发生,最后得到的结果也比较具有说服性.一般讲k取值为10。这样数据稀疏时基本上也能进行。显然,缺点就是训练和测试次数过多。
(3)留一交叉验证(leave one out cross validation)
极端情况下k-折叠交叉验证中k可以取值为m,意味着每次留一个样例做测试,这个称为leave-one-outcross validation(LOO-CV)。
如果设原始数据有N个样本,那么LOO-CV就是N-CV,即每个样本单独作为验证集,其余的N-1个样本作为训练集,所以LOO-CV会得到N个模型,用这N个模型最终的验证集的分类准确率的平均数作为此下LOO-CV分类器的性能指标.相比于前面的K-CV,LOO-CV有两个明显的优点:
1).每一回合中几乎所有的样本皆用于训练模型,因此最接近原始样本的分布,这样评估所得的结果比较可靠。
2).实验过程中没有随机因素会影响实验数据,确保实验过程是可以被复制的。
但LOO-CV的缺点则是计算成本高,因为需要建立的模型数量与原始数据样本数量相同,当原始数据样本数量相当多时,LOO-CV在实作上便有困难几乎就是不显示,除非每次训练分类器得到模型的速度很快,或是可以用并行化计算减少计算所需的时间。
最后,介绍一种与交叉验证有些不同但在各种文献中经常见到的模型选择方法,即自助法(Bootstrap法)。自助法的基本思想是从原始数据中用有放回的抽样方法来产生新的样本,例如我们有一大小为N的数据集,我们从中有放回的抽取N个样本,对N个样本进行建模,每个模型一个预测误差,取N各预测误差的平均值。这一方法在前期介绍C-index值是曾介绍过,C-index中的95%CI就是通过这一方法求出来的。这一方法建议在样本量小且单一的情况下使用。
三、注意事项
交叉验证使用中注意的事项:
1).训练集中样本数量要足够多,一般至少大于总样本数的50%。
2).训练集和测试集必须从完整的数据集中均匀取样。均匀取样的目的是希望减少训练集、测试集与原数据集之间的偏差。当样本数量足够多时,通过随机取样,便可以实现均匀取样的效果。(随机取样,可重复性差)
附:CrossValidation的R实现程序
library(ISLR)
set.seed(1)
str(Auto)
n=nrow(Auto)
train=sample(n,n/2)
test=(-train)
lm.fit=lm(mpg~horsepower, data=Auto, subset=train)
mean((Auto[test,'mpg']-predict(lm.fit, newdata=Auto[test,]))^2)
summary(lm.fit)
##
lm.fit2=lm(mpg~poly(horsepower,2), data=Auto, subset=train)
mean((Auto[test,'mpg']-predict(lm.fit2, newdata=Auto[test,]))^2)
summary(lm.fit2)
###
testError=matrix(NA,10,10)
for(seed in 123:132){
set.seed(seed)
train=sample(n,n/2)
test=(-train)
for(degree in 1:10){
lm.fit=lm(mpg~poly(horsepower,degree), data=Auto,subset=train)
testError[seed-122,degree]=mean((Auto[test,'mpg']-predict(lm.fit,newdata=Auto[test,]))^2)
}
}
range=range(testError)
plot(testError[1,],ylim=range,type='l',col=rainbow(10)[1],xlab='degree',ylab='the estimated test MSE')
for(seed in 2:10)points(testError[seed,],type='l',col=rainbow(10)[seed])
每种颜色代表一次全体样本的随机对半划分
###
##Leave-One-Out Cross-Validation
> cv.error=rep(0,5)
> t1=Sys.time()
> for(degree in 1:5){
+ glm.fit=glm(mpg ~ poly(horsepower,degree), data=Auto)
+ cv.error[degree]=cv.glm(Auto,glm.fit)$delta[1]
+ }
> Sys.time()-t1
Time difference of 45.18859 secs
> cv.error
[1] 24.23151 19.24821 19.33498 19.42443 19.03321
> plot(cv.error,type='b',xlab='degree')
## K-Fold Cross Validation
> set.seed(17)
> cv.error.10=rep(0,10)
> t1=Sys.time()
> for(degree in 1:10){
+ glm.fit=glm(mpg ~ poly(horsepower,degree), data=Auto)
+ cv.error.10[degree]=cv.glm(Auto,glm.fit,K=10)$delta[1]
+ }
> Sys.time()-t1
Time difference of 2.411138 secs
> cv.error.10
[1] 24.20520 19.18924 19.30662 19.33799 18.87911 19.02103 18.8960919.71201 18.95140
[10] 19.50196
> plot(cv.error.10,type='b',xlab='degree')
交叉验证(Cross Validation),有的时候也称作循环估计(Rotation Estimation),是一种统计学上将数据样本切割成较小子集的实用方法,该理论是由Seymour Geisser提出的。
在模式识别(Pattern Recognition)和机器学习(Machine Learning)的相关研究中,经常会将整个数据集合分成两个部分,分别是训练集合和测试集合。假设X是集合全体,A是全集X的非空真子集,那么非空集合X\A则是集合A在全集X中的补集。于是可以先在A上面做训练和分析,而集合X\A则用来做测试和验证。一开始的集合A被称作训练集,而它的补集X\A被称作验证集或者测试集。这里有一个重要的观点就是:只有训练集才可以使用在模型的训练之中,而测试集必须在模型训练完成之后才被用来评估模型的误差。
HoldOut检验(Hold-Out Method)
这个方法是将原始的数据集合X随机分成两个集合A和X\A,其中A作为训练集,X\A作为测试集。先使用训练集训练模型,然后利用测试集验证模型的效果,记录最后的分类准确率作为Hold-Out下该模型的性能指标。比方说,处理时间序列模型是否准确的时候,把整个数据集合分成前后两部分,前部分占比70%,后部分占比30%。前部分来进行时间序列模型的训练,后部分用来测试改时间序列的准确性。其准确性可以用MAE,MAPE之类的统计指标来衡量。综上所述,该方法的好处就是处理起来简单,只需要把原始数据分成两个部分即可。但是从严格意义上来说,Hold-Out检验并不算是交叉检验(Cross Validation),因为该方法没有达到交叉检验的思想,而且最后验证准确性的高低和原始数组的分类有很大的关系,所以该方法得到的结果在某些场景中并不具备特别大的说服力。在Hold-Out检验不够有说服力的情形下,有人提出了交叉验证这一个重要思想。
交叉检验的常见形式
假设有一个未知模型有一个或者多个未知的参数,并且有一个训练集。操作的过程就是对该模型的参数进行调整,使得该模型能够最大的反映训练集的特征。如果模型因为训练集过小或者参数不合适而产生过度拟合的情况,测试集的测试效果就可以得到验证。交叉验证是一种能够预测模型拟合性能的有效方法。
彻底的交叉验证(Exhaustive Cross Validation)
彻底的交叉验证方法指的是遍历全集X的所有非空真子集A。换句话说也就是把A当作训练集,X\A是测试集。如果X中有n个元素,那么非空真子集A的选择方法则是2^{n}-2,这个方法的时间复杂度是指数级别的。
留P验证(Leave-p-out Cross Validation)
留p验证(LpO CV)指的是使用全集X中的p个元素作为测试集,然后剩下的n-p个元素作为训练集。根据数学上的定理可以得到,p个元素的选择方法有n!/((n-p)!p!)个,其中n!表示n的阶乘。在这个意义下,留p验证的时间复杂度也是非常高的。当p=1的时候,留1验证(Leave-one-out Cross Validation)的复杂度恰好是n。
不彻底的交叉验证(Non-exhaustive Cross Validation)
不彻底的交叉验证不需要考虑全集X的所有划分情况,这种方法是留p验证的一个近似验证算法。
k-fold交叉验证(K-fold Cross Validation)
在k-fold交叉验证中,全集X被随机的划分成k个同等大小的集合A1,...,Ak,并且|A1|=...=|Ak|。这里的|Ai|指的是集合Ai的元素个数,也就是集合的势。这个时候需要遍历i从1到k,把X\Ai当作训练集合,Ai当作测试集合。根据模型的测试统计,可以得到Ai集合中测试错误的结果数量ni。如果全集X的势是n的话,可以得到该模型的错误率是E=(ni求和)/n.
为了提高模型的精确度,可以将k-fold交叉验证的上述步骤重复t次,每一次都是随机划分全集
注释:
一般来说,k=10的情况使用得最多。
当k=2的时候,也就是最简单的k-fold交叉验证,2-fold交叉验证。这个时候X是A1和A2的并集,首先A1当训练集并且A2当测试集,然后A2当训练集并且A1当测试集。2-fold交叉验证的好处就是训练集和测试集的势都非常大,每个数据要么在训练集中,要么在测试集中。
当k=n的时候,也就是n-fold交叉验证。这个时候就是上面所说的留一验证(Leave-one-out Cross Validation)。
综上所述,交叉验证(Cross Validation)的好处是可以从有限的数据中获得尽可能多的有效信息,从而可以从多个角度去学习样本,避免陷入局部的极值。在这个过程中,无论是训练样本还是测试样本都得到了尽可能多的学习。
一般模型的选择过程:
在了解了交叉验证的方法之后,可以来介绍一般模型的选择过程。通过采用不同的输入训练样本,来决定机器学习算法中包含的各个参数值,称作模型选择。下面伪代码表示了模型选择的一般流程。在这个算法中,最重要的就是第三个步骤中的误差评价。
(1)准备候选的
(2)对每个模型M1,...,Mq求解它的学习结果。
(3)对每个学习结果的误差e1,...,eq进行计算。这里可以使用上面所说的k-fold交叉验证方法。
(4)选择误差e1,...,eq最小的模型作为最终的模型。
@计量经济学圈
记录一个我们生活在其中的时代社会,一个非常具有潜力的深度与客观兼具的大号,囊括的主题如下:经济、社会、历史、新闻、世界、计量工具。